package com.xukang.utils;

import com.sun.xml.internal.messaging.saaj.util.Base64;
import com.xukang.hex.HexUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Pattern;

/**
 * 数据格式转换
 * 包含 md5，sha256摘要，base64处理，urlEncode处理
 *
 * @author caihongfeng
 * @version 1.0.0
 * @date 2018/08/6
 * @since 1.0.0
 */
public class EncodeUtils {

    private static final Logger logger = LoggerFactory.getLogger(EncodeUtils.class);

    private static final String ENCODE_DIGEST_MD5 = "MD5";
    private static final String ENCODE_DIGEST_SHA256 = "SHA-256";
    private static final String ENCODE_DIGEST_SHA1 = "SHA-1";
    private static final String CHAR_SET_UTF8 = "UTF-8";

    /**
     * 字符串MD5摘要
     *
     * @param data 摘要二进制数组
     * @return 摘要二进制结果
     */
    public static byte[] md5(byte[] data) {
        return digest(ENCODE_DIGEST_MD5, data);
    }


    /**
     * MD5 摘要
     *
     * @param data 摘要字符串
     * @return MD5 摘要结果(字符串)
     */
    public static String md5(String data) {

        try {
            if (StringUtils.isEmpty(data)) {
                return null;
            }
            byte[] result = md5(data.getBytes(CHAR_SET_UTF8));
            return (null == result || result.length < 1) ? null : new String(result, CHAR_SET_UTF8);
        } catch (UnsupportedEncodingException e) {
            logger.error("failed to get data md5 digest unsupport encoding", e);
            throw new RuntimeException("failed to get data md5 digest unsupport encoding");
        }
    }


    /***
     * sm3摘要处理
     * @param data
     * @return
     */
    public static byte[] sm3(byte[] data) {
        return digest("sm3", data);
    }

    public static byte[] sm3(String data, String encode) {
        try {
            return sm3(data.getBytes(encode));
        } catch (Exception e) {
            logger.error("failed to encode data with sha1", e);
            return null;
        }
    }


    /**
     * SHA-1摘要处理
     *
     * @param data 处理数据
     * @return 处理结果
     */
    public static byte[] sha1(byte[] data) {
        return digest(ENCODE_DIGEST_SHA1, data);
    }

    /***
     * SHA-1 摘要处理
     * @param data 处理数据
     * @param encoding 编码
     * @return 处理结果
     */
    public static byte[] sha1(String data, String encoding) {

        try {
            return sha1(data.getBytes(encoding));
        } catch (Exception e) {
            logger.error("failed to encode data with sha1", e);
            return null;
        }
    }

    /**
     * sha256摘要
     *
     * @param data
     * @return
     * @since 1.0.0
     */
    public static byte[] sha256(byte[] data) {
        return digest(ENCODE_DIGEST_SHA256, data);
    }

    /**
     * sha256摘要
     *
     * @param str 摘要字符串
     * @return
     * @since 1.0.0
     */
    public static byte[] sha256(String str, String encoding) {

        try {
            if (StringUtils.isEmpty(str)) {
                return null;
            } else {
                return sha256(str.getBytes(encoding));
            }
        } catch (UnsupportedEncodingException e) {
            logger.error("failed to get data sha256 digest", e);
            throw new RuntimeException("failed to digest sha256 data due to unsupport encoding");
        }
    }


    private static byte[] digest(String alri, byte[] data) {

        if (null == data || data.length < 1) {
            return null;
        }
        if (StringUtils.isEmpty(alri)) {
            throw new IllegalArgumentException("digest algorithm can not be empty");
        }
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(alri);
            return messageDigest.digest(data);
        } catch (NoSuchAlgorithmException e) {
            logger.error("Algorithm UnSupport:" + alri, e);
            throw new RuntimeException("unsupport algorithm" + alri);
        }
    }

    private static final Pattern BASE64_PATTERN = Pattern.compile("^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$");


    public static boolean isBase64String(String str) {
        return BASE64_PATTERN.matcher(str).matches();
    }


    /**
     * base64 字符串格式化
     *
     * @param data
     * @return
     * @since 1.0.0
     */
    public static byte[] base64(byte[] data) {

        if (null == data || data.length < 1) {
            return null;
        }
        return Base64.encode(data);
    }

    /**
     * base64 字符串格式化
     *
     * @param data
     * @return
     * @since 1.0.0
     */
    public static String base64(String data) {

        try {
            if (StringUtils.isEmpty(data)) {
                return null;
            }
            byte[] result = base64(data.getBytes(CHAR_SET_UTF8));
            return (null == result || result.length < 1) ? null : new String(result, CHAR_SET_UTF8);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e.getLocalizedMessage());
        }
    }

    /**
     * 反base64处理
     *
     * @param base64Str
     * @return
     */
    public static byte[] base64Decode(String base64Str) {

        if (!isBase64String(base64Str)) {
            throw new RuntimeException(base64Str + "is not an base64 string");
        }
        return  Base64.encode(base64Str.getBytes());
    }

    /**
     * URL 编码, Encode默认为UTF-8.
     */
    public static String urlEncode(String input) {
        try {
            return URLEncoder.encode(input, CHAR_SET_UTF8);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException("Unsupported Encoding Exception", e);
        }
    }

    /**
     * URL 解码, Encode默认为UTF-8.
     */
    public static String urlDecode(String input) {
        try {
            return URLDecoder.decode(input, CHAR_SET_UTF8);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException("Unsupported Encoding Exception", e);
        }
    }


    /**
     * 字符串16进制化
     *
     * @param data
     * @return
     */
    public static byte[] hexEncode(byte[] data) {

        if (null == data) {
            return null;
        }
        return HexUtil.decode(new String(data));
    }

    /**
     * 字符串反16进制
     *
     * @param data
     * @return
     */
    public static byte[] hexDecode(String data) {

        if (StringUtils.isEmpty(data)) {
            return null;
        }
        return HexUtil.decode(data);
    }

}
